﻿
//Ben Gotthold
// Joe Illuminati
// CISC 360
// 9/23/2013

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace CISC_360_project
{


    public struct Instruction   // structure of an "instruction" for the parser class
    {
        public int instruction_num;     //where the instruction falls in the code 
        public String label;            // If no label, null. else, label name
        public int label_address;       // where the label is located 
        public String instruction;      //the actual string the cpu uses
        public String comment;          //!Comment here
    }


    public class CPU :IDisposable                 // CPU class that contains ALU, Regesters and a path to memory
    {


        public class Regester   // Definition of a regester for cpu
        {
            public String sData;    //if the data is in string form ie. instruction
            public int Data;       // data in int form ie. PC


            public Regester(int i, String s) // constructure to help with inilization 
            {
                this.Data = i;
                this.sData = s;
            }

        }


        #region Contains hardware such as ALU, memory, regesters
        public Memory memory;    // new memory
        ALU alu;                    // new ALU
        #region regesters
        public Regester A;
        public Regester B;
        public Regester Acc;
        public Regester Zero;
        public Regester One;
        public Regester PC;
        public Regester MAR;
        public Regester MDR;
        public Regester Temp;
        public Regester IR;
        public Regester CC;
        public Regester AccD;
        public Regester Ovf;
        public Regester Ret;

        #endregion
        #endregion

        #region global variables
        public int totalNumOpsPreformed = 0;            //keep track of total operations
        public int numOpsForCurrentInstruction = 0;     //keep track for current instruction only
        public Boolean oneWayCache = true;
        public int next_available_mem = 0;
        public Boolean currently_running = true;
        public bool ready_to_cycle = false;

        public int exeCode = -1;
        public int exeValue = 0;
        public int exe_Instr_0 = -1;
        public int exe_Instr_2 = -1;
        public int exe_Instr_3 = -1;

        public int store_i;
        public int store_j = 0;
        public bool store_b = false;

        public bool fetchDone = false;
        public bool decodeDone = false;
        public bool exeDone= false;
        public bool storeDone = false;
     
        public int mulStallCount = 0;
        public bool branchStall = false;
        public bool decodeStall = false;
        public bool mulStall = false;

        public bool incrementPC = true;
        public int totalStallCount = 0;


        #endregion
        #region threads
        private object thread_lock = new object();
        public Thread fetchThread;
        AutoResetEvent fetchEvent = new AutoResetEvent(false);
        public Thread decodeThread;
        AutoResetEvent decodeEvent = new AutoResetEvent(false);
        public Thread executeThread;
        AutoResetEvent executeEvent = new AutoResetEvent(false);
        public Thread storeThread;
        AutoResetEvent storeEvent = new AutoResetEvent(false);

        #endregion

        public CPU() 
        {
            memory = new Memory();    // new memory
            alu = new ALU();          // new ALU
            #region make some regesters
            A = new Regester(0, "");
            B = new Regester(0, "");
            Acc = new Regester(0, "");
            Zero = new Regester(0, "");
            One = new Regester(1, "");
            PC = new Regester(0, "");
            MAR = new Regester(0, "");
            MDR = new Regester(0, "");
            Temp = new Regester(0, "");
            IR = new Regester(0, "");
            CC = new Regester(0, "");
            AccD = new Regester(0, "");
            Ovf = new Regester(0, "");
            Ret = new Regester(0, "");
            #endregion
            #region making threads clearing the thread sets and starting the threads
            fetchThread = new Thread(instructionFetchThreadMethod);
            fetchThread.Name = "fetchThread";
            fetchEvent.Reset();
            fetchThread.Start();
            

            decodeThread = new Thread(instructionDecodeThreadMethod);
            decodeThread.Name = "decodeThread";
            decodeEvent.Reset();
            decodeThread.Start();

            executeThread = new Thread(executeThreadMethod);
            executeThread.Name = "executeThread";
            executeEvent.Reset();
            executeThread.Start();
            

            storeThread = new Thread(resultStoreThreadMethod);
            storeThread.Name = "storeThread";
            storeEvent.Reset();
            storeThread.Start();
            #endregion


        }
        public void Dispose() // cleaning up when we want to destroy te cpu
        {
            currently_running = false;

            fetchEvent.Set();
            decodeEvent.Set();
            executeEvent.Set();
            storeEvent.Set();

            fetchThread.Join();
            decodeThread.Join();
            executeThread.Join();
            storeThread.Join();
            
        }

        #region byte and int convertions
        // Byte[] ={49,3,0,0} then int = 817
        public int convertByteToInt(Byte[] b)   //make ints for regester
        {
            return BitConverter.ToInt32(b, 0);
        }

        //int x = 817 then Byte[] = {49,3,0,0}
        public Byte[] convertIntToByteArray(int i)
        {
            return BitConverter.GetBytes(i);
        }
        #endregion



        public void startExicutionCycle()   //  start the instruction cycle, called by view controler
        {
            Console.WriteLine("starting cycle detemining what to call" );
            if (currently_running)
            {
                storeDone = false;
                numOpsForCurrentInstruction = 0;
                if (mulStall || decodeStall || branchStall) // are we stalling this cycle?
                {
                    totalStallCount++;                      // inc stall counter
                    Console.WriteLine("stall count = " + totalStallCount);
                }

                // determine what instruction should be called based on where we are in the pipline
                // ex) if a store was just called then an exicute is avaiable to be called again
                if (exeDone)
                {
                    storeEvent.Set();
                }
                else if (decodeDone)
                {
                    executeEvent.Set();
                }
                else if (fetchDone)
                {
                    decodeEvent.Set();
                }
                else if(!ready_to_cycle) 
                {
                    fetchEvent.Set();
                }
                else
                {
                    storeEvent.Set();
                }
                this.memory.current_cycle++;
                if (this.memory.current_cycle > this.memory.cycles_until_switch)
                {
                    this.memory.current_cycle = 1;
                    // Save current process
                    this.memory.processes[this.memory.current_process] = saveProcessState(this.memory.processes[this.memory.current_process]);
                    // Flush the cache
                    this.memory.flushCacheWithSave(oneWayCache);
                    // Change process
                    this.memory.current_process = (this.memory.current_process + 1) % 2;
                    // Load new process
                    loadProcesssState(this.memory.processes[this.memory.current_process]);
                }
            }
        }


        #region instruction cycle operations

        #region instruction thread methods
        public void instructionFetchThreadMethod()
        {
            while (currently_running)
            {
                fetchEvent.WaitOne();
                lock (thread_lock)
                {
    
                    instructionFetch();
                }
            }
        }

        public void instructionDecodeThreadMethod()
        {
            while (currently_running)
            {
                decodeEvent.WaitOne();
                lock (thread_lock)
                {
                    instructionDecode();
                }
            }
        }

        public void executeThreadMethod()
        {
            while (currently_running)
            {
                executeEvent.WaitOne();
                lock (thread_lock)
                {
                    execute();
                }
            }
        }

        public void resultStoreThreadMethod()
        {
            while (currently_running)
            {
                storeEvent.WaitOne();
                lock (thread_lock)
                {
                    resultStore();
                }
            }
        }

        #endregion

        private void instructionFetch()     //store current instruction in regester. First IC step
        {
            ready_to_cycle = true;          // is the "assembly line" running
            bool inRangeflag = true;               // for cheking if instruction is in range
            Console.WriteLine("fetch called");
            totalNumOpsPreformed++;
            numOpsForCurrentInstruction++;    // resent cycle count for new instruction


            try
            {
                IR.Data = convertByteToInt(memory.processes[memory.current_process].process.BINARY[PC.Data]);   //get the instruction
            }
            catch (System.IndexOutOfRangeException)     // no instruction to get
            {
                
                inRangeflag = false;                    // instruction not in range
                Console.WriteLine("fetch should stop");
                PC.Data--;                              // not an instruction so go back to lastknown instruction
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            if (incrementPC && !branchStall && inRangeflag)
            {
                PC.Data++;      // if not a branch increment pc
            }
            
            fetchDone = inRangeflag;        // signial compleation if went smooth
        }


        private void instructionDecode()    //current instruction, Literal value or memory lookup?. Second IC step
        {
            if (fetchDone || decodeStall)       // are we stalling on decode, if yes then skip
            {
                Console.WriteLine("decode called");
                totalNumOpsPreformed++;
                numOpsForCurrentInstruction++;
                exe_Instr_0 = convertIntToByteArray(IR.Data)[0];    // save the infromation from the IR
                exe_Instr_2 = convertIntToByteArray(IR.Data)[2];
                exe_Instr_3 = convertIntToByteArray(IR.Data)[3];

                if (convertIntToByteArray(IR.Data)[0] > 8 && convertIntToByteArray(IR.Data)[0] < 16)
                {
                    exeCode = 2;                 // some type of branch instruction
                }
                else if (convertIntToByteArray(IR.Data)[0] == 20 || convertIntToByteArray(IR.Data)[0] == 21)   // set case stall needed
                {
                    if (decodeStall)
                    {
                        operandfetchValue(true);
                        decodeStall = false;
                    }
                    else
                    {
                        Console.WriteLine("decode is stalling on set");
                        decodeStall = true;
                    }
                    
                }
                else if (convertIntToByteArray(IR.Data)[0] == 16) // jmp
                {
                    exeCode = 5;                   
                }
                else if (convertIntToByteArray(IR.Data)[0] == 17) // ret
                {
                    exeCode = 6;              
                }
                else if (convertIntToByteArray(IR.Data)[0] == 18) //no operation is a special case
                {
                    exeCode = 3;
                }
                else if (convertIntToByteArray(IR.Data)[0] == 19)
                {
                    exeCode = 7;
                }
                else if (convertIntToByteArray(IR.Data)[0] == 6) //logical not of acumulator is a special case
                {
                    exeCode = 4;
                }

                else if ((convertIntToByteArray(IR.Data)[1] & 16) == 16)     // imediate value is given in the instruction
                {
                    operandfetchValue();        // will need to load given value into memory

                }
                else if ((convertIntToByteArray(IR.Data)[1] & 16) == 0) // deals with memory
                {
                    if (convertIntToByteArray(IR.Data)[0] == 8)   //store instruction
                    {
                        exeCode = 1;
                    }
                    else
                    {
                        if (decodeStall)
                        {
                            operandfetchMemory();   // will need to get value from memory
                            decodeStall = false;
                        }
                        else
                        {
                            decodeStall = true;
                        }
                    }
                }

                else
                {
                    throw new Exception("invalid instruction"); // not an instruction! should NEVER get here 
                }

                if (!decodeStall)       // if decode is not stalling then get ready to fetch next instruction
                {
                    fetchEvent.Set();   //prepare to fetch
                }
                else
                {
                    Console.WriteLine("decode is stalling");
                }
                decodeDone = true;      // cycled through decode
                    
              
              
            }
            else
            {
                decodeDone = false;     // did not preform a decode
                Console.WriteLine("decode should stop");
            }
            
        }


        private void operandfetchValue(bool set = false)    // the value is given, load into regester A or ACC. sub routine of decode
        {
           

            if (convertIntToByteArray(IR.Data)[0] == 7)       //lda instrcution
            {
               
                store_i = convertIntToByteArray(IR.Data)[3];    // save the value
                exeCode = 3;
                
            }
            else if (set == true)
            {
                if (convertIntToByteArray(IR.Data)[0] == 20)    //set instruction
                {

                    byte[] result = new byte[4];
                    byte[] b = convertIntToByteArray(IR.Data); //extract the value
                    result[0] = b[3];
                    result[1] = b[2];
                    store_i = convertByteToInt(result);     // save the value
                   
                    exeCode = 3;

                    
                }
                else                                           //set hi
                {
                    byte[] result = new byte[4];
                    byte[] lowBits = convertIntToByteArray(Acc.Data);
                    result[0] = lowBits[0];
                    result[1] = lowBits[1];
                    byte[] hiBits = convertIntToByteArray(IR.Data);
                    result[3] = hiBits[2];
                    result[2] = hiBits[3];
                    store_i = convertByteToInt(result);     // save the value
                    

                    exeCode = 3;
                    
                }
            }

            exeCode = 0;
            exeValue = convertIntToByteArray(IR.Data)[3];
  
        }

        private void operandfetchMemory()   //get value from memory, load into regester A or ACC. sub routine of decode
        {
            int dataValue = 0;      // store the value from memory
            if (convertIntToByteArray(IR.Data)[0] == 7)       //lda instrcution
            {
                if (oneWayCache)  // use one way cache
                {
                    store_i = memory.getDataOneWay(convertIntToByteArray(IR.Data)[3]);
                    
                    exeCode = 3;
                    
                   // resultStore(); // load from memory, will need to store in regester Acc
                }
                else  // use two way cache
                {
                    store_i = memory.getDataTwoWay(convertIntToByteArray(IR.Data)[3]);
                    
                    exeCode = 3;
                }
            }
            else
            {

                if (oneWayCache)
                {
                    dataValue = memory.getDataOneWay(convertIntToByteArray(IR.Data)[3]);     // load from memory into regester A
                }
                else
                {
                    dataValue = memory.getDataTwoWay(convertIntToByteArray(IR.Data)[3]);     // load from memory into regester A
                }
            }
            exeCode = 0;            
            exeValue = dataValue;
        }


        private void execute()      // data is in place, preform nessary logic. Third IC step
                                                           // code: 0=ALU, 1=store to memory, 2=branch, 3=nop, 4=nota, 5=jmp, 6=ret, 7=hlt
        {
          
            bool branched = false;  // branched? helps keep track when changing PC

            if (decodeDone || branchStall || mulStall)      // are we stalling in exe?
            {
                Console.WriteLine("execute called");
                prepare_cc();       // update cc based on last store
                totalNumOpsPreformed++;
                numOpsForCurrentInstruction++;

                if (exeCode == 0)                                      // preform alu operation
                {

                    if (exe_Instr_0 == 0)
                    {                                                // add
                        store_i = alu.ADD(Acc.Data, exeValue);
                    }
                    else if (exe_Instr_0 == 1)
                    {
                                                                    // sub
                        store_i = alu.SUB(Acc.Data, exeValue);
                    }
                    else if (exe_Instr_0 == 2)
                    {                                                // and
                        store_i = alu.AND(Acc.Data, exeValue);
                    }
                    else if (exe_Instr_0 == 3)
                    {                                                // or
                        store_i = alu.OR(Acc.Data, exeValue);
                    }

                    else if (exe_Instr_0 == 4)
                    {                                                //multiply 
                        
                        if (mulStallCount == 4)
                        {
                            mulStallCount = 0;
                            mulStall = false;

                            if (Ovf.Data == 0)                          // regester is not overflowed
                            {
                                Tuple<int, int, bool> result = alu.MUL(Acc.Data, exeValue);

                                store_i = result.Item1;     // save the data for store
                                store_j = result.Item2;
                                store_b = result.Item3;
                            }
                            else                                               // regester is not overflowed
                            {
                                byte[] acc = convertIntToByteArray(Acc.Data);      // make byte array acc
                                byte[] accd = convertIntToByteArray(AccD.Data);     //make byte array accd
                                byte[] both = new byte[8];
                                both[0] = acc[0];
                                both[1] = acc[1];
                                both[2] = acc[2];
                                both[3] = acc[3];
                                both[4] = accd[0];
                                both[5] = accd[1];
                                both[6] = accd[2];
                                both[7] = accd[3];
                                Int64 i = BitConverter.ToInt64(both, 0);        // get int 64 of full value
                                Tuple<int, int, bool> result = alu.MUL(i, exeValue);   //multiply


                                store_i = result.Item1;     // save the data for store
                                store_j = result.Item2;
                                store_b = result.Item3;
                            }
                        }
                        else    // stalling on multiply
                        {
                            mulStall = true;
                            mulStallCount++;    //inc multiply stall counter
                            Console.WriteLine("stalling on mul; count = " + mulStallCount);
                        }
                    }
                    else if (exe_Instr_0 == 5)
                    {                                                // divide
                        if (mulStallCount == 4)
                        {
                            mulStallCount = 0;
                            mulStall = false;

                            if (Ovf.Data == 0)                          // regester is not overflowed
                            {
                                Tuple<int, int, bool> result = alu.DIV(Acc.Data, exeValue);

                                store_i = result.Item1;     // save the data for store
                                store_j = result.Item2;
                                store_b = result.Item3;
                            }
                            else                                               // regester is not overflowed
                            {
                                byte[] acc = convertIntToByteArray(Acc.Data);      // make byte array acc
                                byte[] accd = convertIntToByteArray(AccD.Data);     //make byte array accd
                                byte[] both = new byte[8];
                                both[0] = acc[0];
                                both[1] = acc[1];
                                both[2] = acc[2];
                                both[3] = acc[3];
                                both[4] = accd[0];
                                both[5] = accd[1];
                                both[6] = accd[2];
                                both[7] = accd[3];
                                Int64 i = BitConverter.ToInt64(both, 0);        // get int 64 of full value
                                Tuple<int, int, bool> result = alu.DIV(i, exeValue);

                                store_i = result.Item1;     // save the data for store
                                store_j = result.Item2;
                                store_b = result.Item3;
                            }
                        }
                        else     // stalling on divide
                        {
                            mulStall = true;
                            mulStallCount++;
                            Console.WriteLine("stalling on div; count = " + mulStallCount);
                        }
                    }
                }

                else if (exeCode == 1)                                 //store value of acc in memory
                {

                    try
                    {
                        if (oneWayCache)
                        {
                            memory.writeDataOneWay(exe_Instr_3, Acc.Data);           // store the value in memory
                        }
                        else
                        {
                            memory.writeDataTwoWay(exe_Instr_3, Acc.Data);           // store the value in memory
                        }

                    }
                    catch (IndexOutOfRangeException)
                    {
                        throw new Exception("SEGMENTATION FAULT AT LINE \"");// + IR.sData + "\""); // out of range of memory stack
                    }
                }

                else if (exeCode == 2)                                 // branch instruction
                {
                    
                    Console.WriteLine("branch instruction in execute");

                    if (branchStall)        // stalling on branch?
                    {
                        
                        branchStall = false;
                        int adress = exe_Instr_2;         //cast string to int


                        if (exe_Instr_0 == 9)                    //always branch
                        {
                            PC.Data = adress;                          // change PC pointer
                            branched = true;                            // branched
                        }

                        if (exe_Instr_0 == 10)                    //branch when CC = 0
                        {
                            if (CC.Data == 0)
                            {
                                PC.Data = adress;                      // change PC pointer
                                branched = true;                        // branched
                            }
                        }

                        if (exe_Instr_0 == 11)                    // branch when CC = -1
                        {
                            if (CC.Data == -1)
                            {
                                PC.Data = adress;                      // change PC pointer
                                branched = true;                        // branched
                            }
                        }

                        if (exe_Instr_0 == 12)                    // branch when cc = 1
                        {
                            if (CC.Data == 1)
                            {
                                PC.Data = adress;                      // change PC pointer
                                branched = true;                        // branched
                            }
                        }
                        if (exe_Instr_0 == 13)                    // branch greater then or equle
                        {
                            if (CC.Data == 1 || CC.Data == 0)
                            {
                                PC.Data = adress;                      // change PC pointer
                                branched = true;                        // branched
                            }
                        }
                        if (exe_Instr_0 == 14)                    // branch lees then or equal
                        {
                            if (CC.Data == -1 || CC.Data == 0)
                            {
                                PC.Data = adress;                      // change PC pointer
                                branched = true;                        // branched
                            }
                        }
                        if (exe_Instr_0 == 15)                      // branch not equal
                        {
                            if (CC.Data != 0)
                            {
                                PC.Data = adress;                      // change PC pointer
                                branched = true;                        // branched
                            }
                        }
                        if (!branched)
                        {
                            Console.WriteLine("did not take branch");
                        }

                    }
                    else
                    {
                        branchStall = true;
                        Console.WriteLine("stalling!!  cc = " + CC.Data);
                    }
                }

                else if (exeCode == 3)                                 // nop
                {

                    store_i = alu.ADD(Acc.Data, Zero.Data);
                }
                else if (exeCode == 4)                                 // nota
                {

                    store_i = alu.NOTA(Acc.Data);
                }
                else if (exeCode == 5)                                // jmp
                {
                    while (memory.used_memory.Contains(next_available_mem))
                    {
                        next_available_mem++;
                    }
                    if (oneWayCache)
                    {
                        memory.writeDataOneWay(next_available_mem, PC.Data); // put return address into the stack
                    }
                    else
                    {
                        memory.writeDataTwoWay(next_available_mem, PC.Data); // put return address into the stack
                    }


                    memory.stack_frame.Push(next_available_mem);   // put stack address of return address stack frame
                    next_available_mem++;                          // incremement next_available_mem
                    // (if it is not available, the while loop above will catch it next time
                    // a jump is called
                    PC.Data = exe_Instr_2;   // set the PC counter to the jump label
                    incrementPC = false;

                }
                else if (exeCode == 6)
                {

                    int return_address = memory.stack_frame.Pop(); // get location of return address in the stack
                    next_available_mem = return_address;           // set next_available_mem to the stack address we're about to pull from
                    int data = 0;

                    if (oneWayCache)                               // get data from cache
                    {
                        data = memory.getDataOneWay(return_address);
                    }
                    else
                    {
                        data = memory.getDataTwoWay(return_address);
                    }


                    PC.Data = data;        // set the PC back to where the jmp occured
                    incrementPC = true;
                }
                else if (exeCode == 7)  // hlt
                {
                    currently_running = false; // Kill the program
                }

             
                exeDone = true;
                if (!branched && !branchStall && !mulStall)     // if not stalling 
                {
                    decodeEvent.Set();                    // set up for next event
                }
                else           // dont confirm cycle complestion because of stall
                {
                    decodeDone = false;
                    Console.WriteLine("decodevent will not set");
                }
                
            }
            else
            {
                exeDone = false;
                Console.WriteLine("exe should stop");
            }

        }

        private void resultStore()    // store results as neccessary. Fifth IC step
        {
            if (exeDone)   // are we prepared to store?
            {
                Console.WriteLine("store called");
                storeDone = true;
                totalNumOpsPreformed++;
                numOpsForCurrentInstruction++;

                Acc.Data = store_i;                  // set Acc to what ever value 'a' that needs to be stored

                if (store_b)                         // overflow happen?
                {
                    Ovf.Data = 1;
                    AccD.Data = store_j;          // set overflow bits
                }
                else
                {
                    Ovf.Data = 0;           // overflow condition code
                    AccD.Data = 0;
                }
                
                if (decodeDone || branchStall || mulStall)
                {
                    executeEvent.Set();
                }
                else
                {
                    exeDone = false;
                    
                    Console.WriteLine("reseting pipeline for next go");
                    
                }
                
            }
            else      // no more stores should be called
            {

                Console.WriteLine("store should stop");
            }
        }


        private void prepare_cc() // prepare for next IC. subroutine ofexecute so on branch stall condition code is up to date
        {

            if (Acc.Data < 0)
            {
                CC.Data = -1;              // set CC for negitive result
            }
            else if (Acc.Data == 0)
            {
                CC.Data = 0;               // set CC for nutural result
            }
            else
            {
                CC.Data = 1;               // set CC or positive result
            }
            if (incrementPC)                // only if branch did not occur
            {
            }
            else {
                incrementPC = true;
            }

        }

        #endregion

        #region Context Switching

        /// <summary>
        /// Saves the value of the registers in the CPU to the
        /// processState.
        /// </summary>
        /// <param name="state"></param>
        public processState saveProcessState(processState state)
        {
            state.reg_A = this.A.Data;
            state.reg_B = this.B.Data;
            state.reg_Acc = this.Acc.Data;
            state.reg_PC = this.PC.Data;
            state.reg_MAR = this.MAR.Data;
            state.reg_MDR = this.MDR.Data;
            state.reg_Temp = this.Temp.Data;
            state.reg_IR = this.IR.Data;
            state.reg_CC = this.CC.Data;
            state.reg_AccD = this.AccD.Data;
            state.reg_Ovf = this.Ovf.Data;
            state.reg_Ret = this.Ret.Data;
            state.exeCode = this.exeCode;
            state.exeValue = this.exeValue;
            state.exe_Instr_0 = this.exe_Instr_0;
            state.exe_Instr_2 = this.exe_Instr_2;
            state.exe_Instr_3 = this.exe_Instr_3;
            state.store_i = this.store_i;
            state.store_j = this.store_j;
            state.store_b = this.store_b;
            state.fetchDone = this.fetchDone;
            state.decodeDone = this.decodeDone;
            state.exeDone = this.exeDone;
            state.mulStallCount = this.mulStallCount;
            state.branchStall = this.branchStall;
            state.decodeStall = this.decodeStall;
            state.mulStall = this.mulStall;
            state.incrementPC = this.incrementPC;
            state.ready_to_cycle = this.ready_to_cycle;
            return state;
        }

        /// <summary>
        /// Load the values of the state register values into the 
        /// CPUs registers.
        /// </summary>
        /// <param name="state"></param>
        public void loadProcesssState(processState state)
        {
            this.A.Data = state.reg_A;
            this.B.Data = state.reg_B;
            this.Acc.Data = state.reg_Acc;
            this.PC.Data = state.reg_PC;
            this.MAR.Data = state.reg_MAR;
            this.MDR.Data = state.reg_MDR;
            this.Temp.Data = state.reg_Temp;
            this.IR.Data = state.reg_IR;
            this.CC.Data = state.reg_CC;
            this.AccD.Data = state.reg_AccD;
            this.Ovf.Data = state.reg_Ovf;
            this.Ret.Data = state.reg_Ret;
            this.exeCode = state.exeCode;
            this.exeValue = state.exeValue;
            this.exe_Instr_0 = state.exe_Instr_0;
            this.exe_Instr_2 = state.exe_Instr_2;
            this.exe_Instr_3 = state.exe_Instr_3;
            this.store_i = state.store_i;
            this.store_j = state.store_j;
            this.store_b = state.store_b;
            this.fetchDone = state.fetchDone;
            this.decodeDone = state.decodeDone;
            this.exeDone = state.exeDone;
            this.mulStallCount = state.mulStallCount;
            this.branchStall = state.branchStall;
            this.decodeStall = state.decodeStall;
            this.mulStall = state.mulStall;
            this.incrementPC = state.incrementPC;
            this.ready_to_cycle = state.ready_to_cycle;
        }
        #endregion


        class ALU                           // contains arithmatic logic
        {

            public ALU() { }                  // constructor

            #region // ALU logic: add, sub, and, or, nota, mul, div


            public int ADD(int a, int b)
            {       // ADD
                return (a + b);
            }

            public int SUB(int a, int b)        // SUB
            {
                return (a - b);
            }
            public int AND(int a, int b)        // AND
            {
                return (a & b);
            }
            public int OR(int a, int b)         // OR
            {
                return (a | b);
            }
            public int NOTA(int a)
            {             // NOTA
                return (a ^ 1);
            }
            public Tuple<int,int,bool>  MUL(Int64 a, int b)        // MUL
            {

                Int64 c = a * Convert.ToInt64(b);
                
                byte[] result = BitConverter.GetBytes(c);   //byte array of overflowed value
                byte[] small = new byte[4];                 // least significant
                byte[] large = new byte[4];                 // most significant
                bool flag = true;                           // overflow flag
                for(int i = 7; i >= 0; i--)                  // step through array
                {
                    if (i > 3)                              // for high bits
                    {
                        large[(i-4)]= result[i];
                    }
                    else                                    // low bits
                    {
                        small[i] = result[i];
                    }
                }
                if (BitConverter.ToInt32(large, 0) == 0)            // did it overflow?
                {
                    flag = false;                                   //set  flag
                   
                }
                int intsmall = BitConverter.ToInt32(small, 0);
                int intlarge = BitConverter.ToInt32(large, 0);
                return new Tuple<int, int, bool>(intsmall, intlarge, flag);
               
                
            }
            public Tuple<int, int, bool> DIV(Int64 a, int b)        // MUL
            {

                Int64 c = a / Convert.ToInt64(b);

                byte[] result = BitConverter.GetBytes(c);   //byte array of overflowed value
                byte[] small = new byte[4];                 // least significant
                byte[] large = new byte[4];                 // most significant
                bool flag = true;                           // overflow flag
                for (int i = 7; i >= 0; i--)                  // step through array
                {
                    if (i > 3)                              // for high bits
                    {
                        large[(i-4)] = result[i];
                    }
                    else                                    // low bits
                    {
                        small[i] = result[i];
                    }
                }
                if (BitConverter.ToInt32(large, 0) == 0)            // did it overflow?
                {
                    flag = false;                                   //set  flag

                }
                int intsmall = BitConverter.ToInt32(small, 0);
                int intlarge = BitConverter.ToInt32(large, 0);
                return new Tuple<int, int, bool>(intsmall, intlarge, flag);


            }
            

            public int convertByteToInt(Byte[] b)   //make ints for regester
            {
                return BitConverter.ToInt32(b, 0);
            }

            #endregion
        }

    }

}
